import { escapeForRegEx, Range } from '@tiptap/core';
import { ResolvedPos } from '@tiptap/pm/model';

export interface Trigger {
  char: string;
  allowSpaces: boolean;
  allowedPrefixes: string[] | null;
  startOfLine: boolean;
  $position: ResolvedPos;
}

export type SuggestionMatch = {
  range: Range;
  query: string;
  text: string;
} | null;

export function findSuggestionMatch(config: Trigger): SuggestionMatch {
  const { char, allowSpaces, startOfLine, $position } = config;

  const escapedChar = escapeForRegEx(char);
  const suffix = new RegExp(`${escapedChar}$`);
  const prefix = startOfLine ? '^' : '';
  const regexp = allowSpaces
    ? new RegExp(`${prefix}${escapedChar}.*?(?=${escapedChar}|$)`, 'gm')
    : new RegExp(`${prefix}(?:^)?${escapedChar}[^\\s${escapedChar}]*`, 'gm');

  const text = $position.nodeBefore?.isText && $position.nodeBefore.text;

  if (!text) {
    return null;
  }

  const textFrom = $position.pos - text.length;
  const match = Array.from(text.matchAll(regexp)).pop();

  if (!match || match.input === undefined || match.index === undefined) {
    return null;
  }

  // The absolute position of the match in the document
  const from = textFrom + match.index;
  let to = from + match[0].length;

  // Edge case handling; if spaces are allowed and we're directly in between
  // two triggers
  if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) {
    match[0] += ' ';
    to += 1;
  }

  // If the $position is located within the matched substring, return that range
  if (from < $position.pos && to >= $position.pos) {
    return {
      range: {
        from,
        to,
      },
      query: match[0].slice(char.length),
      text: match[0],
    };
  }

  return null;
}
